//pinmux  gpio_3_1  himm 0x12040090 0x0
//pinmux  gpio_3_3  himm 0x12040088 0x0


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/mman.h>

#define SHT_DEBUG 0

#define noACK 	0
#define ACK   	1			//adr  command  r/w
#define STATUS_REG_W 0x06   //000   0011    0
#define STATUS_REG_R 0x07   //000   0011    1
#define MEASURE_TEMP 0x03   //000   0001    1
#define MEASURE_HUMI 0x05   //000   0010    1
#define RESET        0x1e   //000   1111    0

#define IO_BASE_ADDR 0x12143000
#define DIR_OFFSET 0x400
#define DATA_CLK_OFFSET 0x8 //GPIO3_1
#define DATA_SDA_OFFSET 0x20 //GPIO3_3

#define DELAY_20MS 2000
//gpio3_1 0x8
//gpio3_3 0x20


enum {TEMP,HUMI};

static void *io_addr;
static int mem_fd = -1;

int sht_addr_init(void)
{
    if (mem_fd > 0)
	return 0;

    mem_fd = open ("/dev/mem", O_RDWR | O_SYNC);
    if (mem_fd <= 0)
    {
	printf("open mem fd err!\n");
	return -1;
    }

    if (io_addr)
	return 0;
    io_addr = mmap (0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, IO_BASE_ADDR);
    printf("io remap: io_addr is %x\n", io_addr);
    return 0;
}

int sht_addr_deinit(void)
{
    if (!io_addr)
	return;

    munmap(io_addr , 4096);
    close(mem_fd);
    return 0;
}


void set_clk_dir(char val)
{
    volatile unsigned char reg = 0;

    if (!io_addr)
	return ;

    reg = *(volatile unsigned char *)(io_addr + DIR_OFFSET); 
    reg |= 0x2;
#if SHT_DEBUG
    printf("%s set %x\n", __func__, reg);
#endif
    *(volatile unsigned char *)(io_addr + DIR_OFFSET) = reg;

    return;
}

void set_sda_dir(char val)
{
    volatile unsigned char reg = 0;

    if (!io_addr)
	return ;

    reg = *(volatile unsigned char *)(io_addr + DIR_OFFSET); 
    if(val)
	reg |= 0x8;
    else
	reg &= 0xf7;
#if SHT_DEBUG
    printf("%s set %x\n", __func__,reg);
#endif
    *(volatile unsigned char *)(io_addr + DIR_OFFSET) = reg;
}

void inline set_sda(char val)
{
    volatile unsigned char reg = 0;
    if (!io_addr)
	return ;
    reg = *(volatile unsigned char *)(io_addr + DATA_SDA_OFFSET);
    if(val)
	reg |= 0x8;
    else
    {
	reg = reg & 0xf7;
    }
    *(volatile unsigned char *)(io_addr + DATA_SDA_OFFSET) = reg; 
#if SHT_DEBUG
    printf("%s set %x\n", __func__,reg);
#endif
    return;
}

char get_sda(void)
{
    if (!io_addr)
	return ;

    return *(volatile unsigned char *)(io_addr + DATA_SDA_OFFSET); 
}

void inline set_clk(char val)
{

    if (!io_addr)
	return ;

    volatile unsigned char reg = 0;
    reg = *(volatile unsigned char *)(io_addr + DATA_CLK_OFFSET);
    if(val)
	reg |= 0x2;
    else
    {
	reg = reg & 0xfd;//0x11111101
    }
    *(volatile unsigned char *)(io_addr + DATA_CLK_OFFSET) = reg; 
#if SHT_DEBUG
    printf("%s set %x\n", __func__,reg);
#endif
}

//----------------------------------------------------------------------------------
char sht_write_byte(unsigned char value)
    //----------------------------------------------------------------------------------
    // writes a byte on the Sensibus and checks the acknowledge 
{ 
    unsigned char i,  error=0; 

    unsigned char n;

    for (i=0x80;i>0;i/=2)             	//shift bit for masking
    { 
	usleep(DELAY_20MS) ;    	//pulswith approx. 5 us   
	if (i & value) 
	    set_sda(1);        	//masking value with i , write to SENSI-BUS
	else
	    set_sda(0);        	//masking value with i , write to SENSI-BUS

	usleep(DELAY_20MS) ;    	//pulswith approx. 5 us   

	set_clk(1);                        	//clk for SENSI-BUS

	usleep(DELAY_20MS) ;    	//pulswith approx. 5 us   
	
	set_clk(0);                        	//clk for SENSI-BUS

	//usleep(DELAY_20MS);
    }

    //release data line
    set_sda(1);
    usleep(100);
    set_sda_dir(0);         
    usleep(DELAY_20MS);
    
    set_clk(1);                            	//clk #9 for ack 
    usleep(DELAY_20MS);
    
    error=get_sda();
#if SHT_DEBUG
    printf("check sht ack is %x\n", error);
    //error=DATA;                       	//check ack (DATA will be pulled down by SHT11)
#endif
    
    usleep(DELAY_20MS);
    set_clk(0);                        	//clk for SENSI-BUS
    usleep(DELAY_20MS);

    return error;                     	//error=1 in case of no acknowledge
}

//----------------------------------------------------------------------------------
char sht_read_byte(unsigned char ack)
    //----------------------------------------------------------------------------------
    // reads a byte form the Sensibus and gives an acknowledge in case of "ack=1" 
{ 
    unsigned char i,val=0, read;
    //set_sda_dir(1);
    //set_sda(1);
    usleep(DELAY_20MS);
    set_sda_dir(0);
    //DATA=1;                           	//release DATA-line
    for (i=0x80;i>0;i/=2)             	//shift bit for masking
    { 
	set_clk(1);
	usleep(DELAY_20MS);
	read = get_sda();
#if SHT_DEBUG
	printf("read sda is %x\n", read);
#endif 
	if (read) 
	{
	    val=(val | i);      	//read bit  
#if SHT_DEBUG
	    printf("read val is 0x%x\n", val);
#endif
	}

	set_clk(0);
	usleep(DELAY_20MS);
    }
    //DATA=!ack;                        	//in case of "ack==1" pull down DATA-Line
    //set_sda(!ack); //FIXME
    set_sda_dir(1);
    set_sda(!ack);

    usleep(DELAY_20MS);

    set_clk(1);
    usleep(DELAY_20MS);
    set_clk(0);
    set_sda_dir(0);

    usleep(DELAY_20MS);
    set_sda_dir(1); //control DATA line
    set_sda(1);
    usleep(DELAY_20MS);

    return val;
}

//----------------------------------------------------------------------------------
void sht_transstart(void)
//----------------------------------------------------------------------------------
// generates a transmission start 
//		  1 2 3 4	5 6	7
//       _____         ________
// DATA:      |_______|
//           ___     ___
// SCK : ___|   |___|   |______
{  
    set_sda(1); 
    usleep(DELAY_20MS);

    set_clk(0);          	//1----Initial state
    usleep(DELAY_20MS);
    
    set_clk(1);          	//1----Initial state
    usleep(DELAY_20MS);

    set_sda(0); 
    usleep(DELAY_20MS);
    
    set_clk(0);          	//1----Initial state
    usleep(DELAY_20MS);
    
    set_clk(1);          	//1----Initial state
    usleep(DELAY_20MS);

    set_sda(1); 
    usleep(DELAY_20MS);

    set_clk(0);          	//1----Initial state
    usleep(DELAY_20MS);
}

//----------------------------------------------------------------------------------
void sht_connectionreset(void)
//----------------------------------------------------------------------------------
// communication reset: DATA-line=1 and at least 9 SCK cycles followed by transstart
//       _____________________________________________________         ________
// DATA:                                                      |_______|
//          _    _    _    _    _    _    _    _    _        ___     ___
// SCK : __| |__| |__| |__| |__| |__| |__| |__| |__| |______|   |___|   |______
{  
  	unsigned char i; 
  	int n;
	set_sda(1);
	usleep(DELAY_20MS);
	
	set_clk(0);
	usleep(DELAY_20MS);

	for(i=0;i<9;i++)                  	//9 SCK cycles
	{ 
	    set_clk(1);
	    usleep(DELAY_20MS);
	    set_clk(0);
	    usleep(DELAY_20MS);
	}
	sht_transstart();                   	//transmission start
}

           
//----------------------------------------------------------------------------------
unsigned short sht_measure(unsigned char *p_value, unsigned char *p_checksum, unsigned char mode)
//----------------------------------------------------------------------------------
// makes a measurement (humidity/temperature) with checksum
{ 
    unsigned int error=0;
    unsigned int i;
    unsigned short sens_value = 0;
     float temp, humi;

    int count = 0;
    sht_transstart();                   	//transmission start
    switch(mode)
    {                     				//send command to sensor
	case TEMP : error+=sht_write_byte(MEASURE_TEMP); break;
	case HUMI : error+=sht_write_byte(MEASURE_HUMI); break;
	default   : break;  
    }
    usleep(DELAY_20MS*50);
    while (1) 
    {	
	usleep(DELAY_20MS);
	//set_sda_dir(0);
	if(get_sda()==0) 
	{
	    printf("data ready!\n");
	    break; //wait until sensor has finished the measurement
	}
	else
	{
	    printf("wait\n");
	    count++;
	    if(count >20)
	    {
		printf("get data timeout!\n");
	    }
	}
    }
    /*
    if(get_sda()) 
	error+=1;                	// or timeout (~2 sec.) is reached
	*/
    *(p_value)  =sht_read_byte(ACK);    	//read the first byte (MSB)
    *(p_value+1)=sht_read_byte(ACK);    	//read the second byte (LSB)

    *p_checksum =sht_read_byte(noACK);  //read checksum

    printf("read p_value is %x, p_value+1 is %x, p_checksum is %x\n",*p_value, *(p_value+1), *p_checksum);
    sens_value = ((0xff) & *(p_value))<<8 | ((0xff)&*(p_value+1));
    printf("sens_value is %x\n", sens_value);
    
    return sens_value;
}

int main()
{
    unsigned char value;
    unsigned char checksum;
    unsigned short temp, humi;
    float f_temp=0, f_humi=0;

    const float C1=-4.0;                    // for 12 Bit 湿度修正公式
    const float C2=+0.0405;                 // for 12 Bit 湿度修正公式
    const float C3=-0.0000028;              // for 12 Bit 湿度修正公式
    const float T1=+0.01;                   // for 14 Bit @ 5V 温度修正公式
    const float T2=+0.00008;                // for 14 Bit @ 5V 温度修正公式

    float rh_lin;                           // rh_lin:  Humidity linear
    float rh_true;                          // rh_true: Temperature compensated humidity

    printf("start: \n");
    sht_addr_init();
    set_clk_dir(1);
    set_sda_dir(1);
    printf("start test temp!\n");
    sht_connectionreset();
    temp = sht_measure(&value, &checksum, TEMP);
    set_clk_dir(1);
    set_sda_dir(1);
//    usleep(DELAY_20MS*100);
    printf("start test humi!\n");
    sht_connectionreset();
    humi = sht_measure(&value, &checksum, HUMI);
    f_temp = temp * 0.01 - 40;
    
    rh_lin=C3*humi*humi + C2*humi + C1;
    rh_true=(f_temp-25)*(T1+T2*humi)+rh_lin;
    if(rh_true > 100)
	rh_true = 100;
    if(rh_true < 1)
	rh_true = 1;

    printf("temp is %f, humi is %f\n", f_temp, rh_true);
    sht_addr_deinit();
    printf("end: \n");
}
